using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using System.Drawing.Drawing2D;

namespace WbsEditor
{
    class WbsRenderer
    {
        private WbsRenderer()
        {
        }

        #region Static public methods
        public static void RenderWbs(WbsNode root, WbsSettings settings, Graphics g, 
                                     WorkBreakdownStructure wbs, Size? pixelSize,
                                     string currentIteration)
        {
            RenderTree(root, settings, g, wbs, currentIteration);
        }
        #endregion

        #region Private methods
        private static void RenderTree(WbsNode node, WbsSettings settings, Graphics g,
                                       WorkBreakdownStructure wbs, string currentIteration)
        {
            RenderNode(node, settings, g, wbs, currentIteration);
            if (!node.IsCollapsed)
            {
                foreach (WbsNode childNode in node.ChildNodes)
                {
                    RenderTree(childNode, settings, g, wbs, currentIteration);
                }
            }
            if (node.ChildNodes.Count > 0)
            {
                RenderConnections(node, settings, g);
            }
        }

        private static Font _workItemTypeFont = new Font("Arial Narrow", 20, FontStyle.Bold);
        private static Font _workItemTypeFontSmall = new Font("Arial Narrow", 12, FontStyle.Bold);

        public static void RenderNode(WbsNode node, WbsSettings settings, Graphics g,
                                       WorkBreakdownStructure wbs, string currentIteration)
        {
            Pen pen = Pens.Black;
            bool highlight = settings.HighlightName != null &&
                node.AssignedTo == settings.HighlightName &&
                (node.IsLeaf || settings.HighlightName.CompareTo("") != 0);
            bool findHighlight = !string.IsNullOrEmpty(settings.FindString) &&
                node.Title.ToLower().Contains(settings.FindString.ToLower());
            bool iterationHighlight = settings.HighlightCurrentIteration &&
                (node.Iteration == currentIteration);
            // Shadow
            g.FillRectangle(Brushes.DarkGray,
                    node.LayoutInfo.Position.X + settings.ShadowOffset,
                    node.LayoutInfo.Position.Y + settings.ShadowOffset,
                    node.LayoutInfo.Size.Width,
                    node.LayoutInfo.Size.Height);
            Brush bk = Brushes.White;
            if (node.IsCollapsed)
            {
                bk = Brushes.LightSalmon;
            }
            if (highlight)
            {
                bk = Brushes.LightSkyBlue;
            }
            if (findHighlight)
            {
                bk = new SolidBrush(settings.FindResultColor);
            }
            if (iterationHighlight)
            {
                bk = Brushes.LightSkyBlue;
            }

            g.FillRectangle(bk,
                node.LayoutInfo.Position.X,
                node.LayoutInfo.Position.Y,
                node.LayoutInfo.Size.Width,
                node.LayoutInfo.Size.Height);
            if (node.IsSelected)
            {
                g.FillRectangle(Brushes.LightGray,
                    node.LayoutInfo.Position.X,
                    node.LayoutInfo.Position.Y,
                    node.LayoutInfo.Size.Width,
                    node.LayoutInfo.Size.Height);
                pen = Pens.Red;
            }
            if (!string.IsNullOrEmpty(node.PlausiText)
                && settings.CheckPlausis)
            {
                float size = 30;
                float midX = node.LayoutInfo.Position.X + node.LayoutInfo.Size.Width / 2;
                float midY = node.LayoutInfo.Position.Y + node.LayoutInfo.Size.Height / 2;
                Brush brush = null;
                if (node.PlausiText.StartsWith("!"))
                    brush = new SolidBrush(Color.Red);
                else
                    brush = new SolidBrush(settings.PlausiMarkColor);
                g.FillEllipse(brush, midX - size / 2f, midY - size / 2f, size, size);

                g.FillRectangle(Brushes.White, midX - 2, midY - size / 2f + 4, 5, size - 14);
                g.FillRectangle(Brushes.White, midX - 2, midY + size / 2f - 8, 5, 5);
            }

            if (node.IsLeaf)
            {
                Brush typeBrush = node.IsSelected ? Brushes.White : Brushes.LightGray;
                Font typeFont = WbsSettings.LayoutSize.Small == settings.Layout ? _workItemTypeFontSmall : _workItemTypeFont;
                StringFormat centerAlign = new StringFormat();
                centerAlign.Alignment = StringAlignment.Center;
                centerAlign.LineAlignment = StringAlignment.Center;
                g.DrawString(node.WorkItemType, typeFont, typeBrush, new Rectangle(node.LayoutInfo.Position, node.LayoutInfo.Size), centerAlign);
            }

            if (node.CompletedWorkInDays > 0 || node.InnerNodeCompletedWorkInDays > 0)
            {
                float totalWork = 0f;
                float workWidth = 0f;
                Brush color = new SolidBrush(settings.ProgressBarColor);
                if (node.CompletedWorkInDays > 0)
                {
                    totalWork = node.CompletedWorkInDays + node.RemainingWorkInDays;
                    workWidth = node.CompletedWorkInDays / totalWork * node.LayoutInfo.Size.Width;
                }
                else
                {
                    totalWork = node.InnerNodeCompletedWorkInDays + node.InnerNodeRemainingWorkInDays;
                    workWidth = node.InnerNodeCompletedWorkInDays / totalWork * node.LayoutInfo.Size.Width;
                    color = new SolidBrush(settings.InnerProgressColor);
                }
                g.FillRectangle(color, node.LayoutInfo.Position.X,
                    node.LayoutInfo.Position.Y, workWidth, settings.NormalFont.Size + 2);
            }
            g.DrawRectangle(pen, 
                node.LayoutInfo.Position.X,
                node.LayoutInfo.Position.Y,
                node.LayoutInfo.Size.Width,
                node.LayoutInfo.Size.Height);
            if (node.IsPublished)
            {
                g.DrawRectangle(pen,
                    node.LayoutInfo.Position.X - 1,
                    node.LayoutInfo.Position.Y - 1,
                    node.LayoutInfo.Size.Width + 2,
                    node.LayoutInfo.Size.Height + 2);
            }
            RectangleF topBox = new RectangleF(node.LayoutInfo.Position.X, node.LayoutInfo.Position.Y,
                node.LayoutInfo.Size.Width, settings.SmallFont.Size + 3f);
            g.DrawString(node.PspCode, settings.SmallFont, Brushes.Blue, 
                topBox);
            StringFormat rightAlign = new StringFormat();
            rightAlign.Alignment = StringAlignment.Far;
            if (node.IsPublished)
            {
                g.DrawString(string.Format("#{0}", node.WorkItemId), settings.SmallFont, Brushes.Blue,
                    topBox, rightAlign);
            }
            RectangleF layoutRect = new RectangleF(node.LayoutInfo.Position.X, node.LayoutInfo.Position.Y + settings.NormalFont.Size,
                node.LayoutInfo.Size.Width, node.LayoutInfo.Size.Height - 2*settings.NormalFont.Size);
            g.DrawString(node.Title, settings.NormalFont, new SolidBrush(settings.TitleColor), layoutRect);
            RectangleF assignedToRect = new RectangleF(layoutRect.Left, node.LayoutInfo.Position.Y + node.LayoutInfo.Size.Height - (settings.SmallFont.Size + 5),
                layoutRect.Width, settings.SmallFont.Size + 5);
            g.DrawString(node.AssignedTo, settings.SmallFont, Brushes.Gray, assignedToRect);
            RectangleF remWorkRect = new RectangleF(layoutRect.Left, node.LayoutInfo.Position.Y + node.LayoutInfo.Size.Height - (settings.SmallFont.Size + 5),
                layoutRect.Width, settings.SmallFont.Size + 5);
            if (node.ChildNodes.Count == 0 || node.IsRoot)
            {
                g.DrawString(MakeWorkAmountString(node, wbs), settings.SmallFont, Brushes.Black, remWorkRect, rightAlign);
            }
            else
            {
                g.DrawString(MakeWorkAmountString(node, wbs), settings.SmallFont, Brushes.Gray, remWorkRect, rightAlign);
            }
            if ((node.RemainingWorkInDays <= 0.01f && node.CompletedWorkInDays > 0f)
                || (node.InnerNodeRemainingWorkInDays <= 0.01f && node.InnerNodeCompletedWorkInDays > 0f))
            {
                g.DrawLine(Pens.Black,
                    node.LayoutInfo.Position.X + 1,
                    node.LayoutInfo.Position.Y + node.LayoutInfo.Size.Height - 2,
                    node.LayoutInfo.Position.X + node.LayoutInfo.Size.Width - 2,
                    node.LayoutInfo.Position.Y + 1);
            }
        }

        private static string MakeWorkAmountString(WbsNode node, WorkBreakdownStructure wbs)
        {
            if (WorkBreakdownStructure.TimeUnit.Days == wbs.DisplayUnit)
                return string.Format("{0} PD", node.RemainingWorkInDays);
            float hours = node.RemainingWorkInDays * wbs.DaysToHoursFactor;
            return string.Format("{0} h", hours);

        }

        private static void RenderConnections(WbsNode node, WbsSettings settings, Graphics g)
        {
            if (node.ChildNodes.Count == 0)
                return;
                
            Pen pen = Pens.SlateGray;
            if (node.IsRoot)
            {
                int centerX = node.LayoutInfo.Position.X + node.LayoutInfo.Size.Width / 2;
                Rectangle bb = node.LayoutInfo.BoundingBox;
                int middleY = bb.Bottom + settings.VerticalPaddingAtRoot / 2;
                g.DrawLine(pen, centerX, bb.Bottom + 1, centerX, middleY);
                WbsNode leftMost = node.ChildNodes[0];
                WbsNode rightMost = node.ChildNodes[node.ChildNodes.Count - 1];
                int leftCenterX = leftMost.LayoutInfo.Position.X + leftMost.LayoutInfo.Size.Width / 2;
                int rightCenterX = rightMost.LayoutInfo.Position.X + rightMost.LayoutInfo.Size.Width / 2;
                if (node.ChildNodes.Count == 1)
                {
                    if (centerX > leftCenterX)
                        rightCenterX = centerX;
                    else
                        leftCenterX = centerX;
                }
                g.DrawLine(pen, leftCenterX, middleY, rightCenterX, middleY);
                int bottomY = bb.Bottom + settings.VerticalPaddingAtRoot - 1;
                foreach (WbsNode child in node.ChildNodes)
                {
                    int nodeMiddleX = child.LayoutInfo.Position.X + child.LayoutInfo.Size.Width / 2;
                    g.DrawLine(pen, nodeMiddleX, middleY, nodeMiddleX, bottomY);
                }
            }
            else
            {
                if (!node.IsCollapsed)
                {
                    int leftX = node.LayoutInfo.Position.X + settings.IndentationVerticalLayout / 2;
                    WbsNodeLayoutInformation bottomMost = node.ChildNodes[node.ChildNodes.Count - 1].LayoutInfo;
                    int topMostY = node.LayoutInfo.Position.Y + node.LayoutInfo.Size.Height + 1;
                    int bottomMostY = bottomMost.Position.Y + bottomMost.Size.Height / 2;
                    g.DrawLine(pen, leftX, topMostY, leftX, bottomMostY);
                    foreach (WbsNode child in node.ChildNodes)
                    {
                        int nodeMiddleY = child.LayoutInfo.Position.Y + child.LayoutInfo.Size.Height / 2;
                        g.DrawLine(pen, leftX, nodeMiddleY, child.LayoutInfo.Position.X - 1, nodeMiddleY);
                    }
                }
                if (node.HasChildNodes)
                {
                    int leftX = node.LayoutInfo.CollapseBox.X;
                    int middleX = node.LayoutInfo.CollapseBox.X + node.LayoutInfo.CollapseBox.Width / 2;
                    int belowNodeY = node.LayoutInfo.CollapseBox.Y;
                    int middleY = node.LayoutInfo.CollapseBox.Y + node.LayoutInfo.CollapseBox.Height / 2;
                    int width = node.LayoutInfo.CollapseBox.Width;
                    int height = node.LayoutInfo.CollapseBox.Height;
                    g.FillRectangle(Brushes.White, leftX, belowNodeY, width, height);
                    g.DrawRectangle(Pens.Black, leftX, belowNodeY, width, height);
                    g.DrawLine(Pens.Black, leftX, middleY, leftX + width, middleY);
                    if (node.IsCollapsed)
                    {
                        g.DrawLine(Pens.Black, middleX, belowNodeY, middleX, belowNodeY + height);
                    }
                }
            }
        }
        #endregion
    }
}
